﻿// Game.cpp: 实现文件
//

#include "pch.h"
#include "Menu.h"
#include "afxdialogex.h"
#include "Game.h"
#include "Snake.h"
#include "resource.h"
#define IDPAUSE 999
#define EASY 1
#define MEDIUM 2
#define HARD 3

#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4

#define NEW 0
#define CONTINUE 1

#define FPS_CLOCK 1
#define UPDATESNAKE_CLOCK 2

// Game 对话框
/***
* 游戏界面窗口
* 实现功能：
* 创建新游戏实例
* 游戏背景以及游戏对象的绘制
* 读取用户操作并更改游戏实例
*/

IMPLEMENT_DYNAMIC(Game, CDialogEx)

//重载构造函数，传入游戏运行模式
Game::Game(CWnd* pParent /*=nullptr*/, int mode)
	: CDialogEx(IDD_GAME_DIALOG, pParent)
{
	mSnake = NULL;
	direction = 4;
	gameOver = false;
	this->mode = mode;
}

Game::~Game()
{
}

void Game::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(Game, CDialogEx)
	ON_WM_GETMINMAXINFO()
	ON_WM_PAINT()
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_BUTTON_PAUSE, &Game::OnBnClickedButtonpause)
	ON_BN_CLICKED(IDC_BUTTON_BREAK, &Game::OnBnClickedButtonBreak)
	ON_WM_TIMER()
	ON_WM_ERASEBKGND()
	ON_WM_CLOSE()
END_MESSAGE_MAP()


// Game 消息处理程序


BOOL Game::OnInitDialog()
{
	CDialogEx::OnInitDialog();


	CBitmap bmp;
	bmp.LoadBitmap(IDB_GAME_BACKGROUD);								//读取游戏背景
	mBackGroundBrush.CreatePatternBrush(&bmp);								//创建匹配画刷
	mSnake = new Snake();											//实例化新游戏对象



	//使用NEW模式创建游戏
	if (mode == NEW) {
		int difficuty = GetPrivateProfileInt(_T("SETTING"), _T("DIFFICUTY"), 0, _T("./Setting.ini"));
		switch (difficuty)
		{
		case EASY:
			mSnake->speed = 1;
			break;
		case MEDIUM:
			mSnake->speed = 5;
			break;
		case HARD:
			mSnake->speed = 7;
			break;
		}
	}
	//使用CONTINUE模式创建游戏
	else if (mode == CONTINUE) {
		//重新初始化蛇
		mSnake->body.clear();																						//清除已有蛇身
		mSnake->speed = GetPrivateProfileInt(_T("SETTING"), _T("SPEED"), 0, _T("./Setting.ini"));					//读取蛇速度
		mSnake->direction = GetPrivateProfileInt(_T("SETTING"), _T("DIRECTION"), 0, _T("./Setting.ini"));			//读取蛇方向
		direction = mSnake->direction;
		
		//读取已有蛇身
		int bodyAmount = GetPrivateProfileInt(_T("SNAKE"), _T("BODYAmount"), 0, _T("./Setting.ini"));				//读取蛇身长度
		CString bodyX, bodyY;
		for (int i = 1; i <= bodyAmount; ++i) {
			bodyX.Format(_T("BODY%dX"), i);
			bodyY.Format(_T("BODY%dY"), i);
			int x = GetPrivateProfileInt(_T("SNAKE"), bodyX, 0, _T("./Setting.ini"));
			int y = GetPrivateProfileInt(_T("SNAKE"), bodyY, 0, _T("./Setting.ini"));
			mSnake->body.push_back({ x, y });
		}


		//读取已有食物
		int itemAmount = GetPrivateProfileInt(_T("MAP"), _T("ITEMAmount"), 0, _T("./Setting.ini"));					//读取食物数量
		CString itemX, itemY;
		for (int i = 1; i <= itemAmount; ++i) {
			itemX.Format(_T("ITEM%dX"), i);
			itemY.Format(_T("ITEM%dY"), i);
			int x = GetPrivateProfileInt(_T("MAP"), itemX, 0, _T("./Setting.ini"));
			int y = GetPrivateProfileInt(_T("MAP"), itemY, 0, _T("./Setting.ini"));
			mSnake->item.push_back({ x, y });
		}
	}


	//初始化时钟
	SetTimer(FPS_CLOCK, 16, NULL);											//每16ms刷新一次屏幕
	SetTimer(UPDATESNAKE_CLOCK, 500 - (mSnake->speed - 1) * 50, NULL);		//设置刷新蛇的时长，用于控制蛇速度
	GetDlgItem(IDC_STATIC_SCORE)->GetFocus();								//设置对话框焦点为文本


	return FALSE;  // return TRUE unless you set the focus to a control
	// 异常: OCX 属性页应返回 FALSE
}


void Game::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
	lpMMI->ptMinTrackSize = {1024, 815};		//设置窗口大小
	CDialogEx::OnGetMinMaxInfo(lpMMI);
}


void Game::OnPaint()
{
	CPaintDC dc(this); // device context for painting


	//双缓冲绘制预处理工作
	CDC dcMem;
	CBitmap bmp;
	CRect windowRect;
	GetClientRect(&windowRect);
	bmp.CreateCompatibleBitmap(&dc, windowRect.Width(), windowRect.Height());		//创建用于绘制的图像
	dcMem.CreateCompatibleDC(&dc);													//创建用于绘制的设备
	dcMem.SelectObject(&bmp);


	//绘制背景
	CBrush *oldBrush = dcMem.SelectObject(&mBackGroundBrush);								
	dcMem.Rectangle(windowRect);
	dcMem.SelectObject(oldBrush);


	//绘制食物
	int lenX = windowRect.Width() * 0.8 / 34; 
	int lenY = windowRect.Height() / 34;
	CBrush brush(RGB(255, 255, 0));
	dcMem.SelectObject(brush);
	for (auto it = mSnake->item.begin(); it != mSnake->item.end(); ++it) {
		int i = it->first;
		int j = it->second;
		dcMem.MoveTo(0, 0);
		dcMem.Rectangle(i * lenX, j * lenY, (i + 1) * lenX, (j + 1) * lenY);
	}


	//绘制蛇身
	CBrush snakeBodyBrush(RGB(0, 197, 207));
	CBrush snakeHeadBrush(RGB(224, 47, 47));
	dcMem.SelectObject(snakeBodyBrush);
	for (auto it = mSnake->body.begin(); it != mSnake->body.end(); ++it) {
		dcMem.MoveTo(0, 0);
		if (next(it) == mSnake->body.end()) {
			dcMem.SelectObject(snakeHeadBrush);
		}
		dcMem.Rectangle(it->second * lenX, it->first * lenY, (it->second + 1) * lenX, (it->first + 1) * lenY);
	}


	//绘制分割线
	CPen pen(PS_SOLID, 3, RGB(255, 0, 0));
	dcMem.SelectObject(pen);
	POINT pst, ped;
	pst = { (long)(windowRect.Width() * 0.8), 0 };
	ped = { (long)(windowRect.Width() * 0.8), windowRect.Height() };
	dcMem.MoveTo(pst);
	dcMem.PolylineTo(&ped, 1);


	//双缓冲绘制结束工作
	dc.BitBlt(0, 0, windowRect.Width() * 0.803, windowRect.Height(), &dcMem, 0, 0, SRCCOPY);		//复制图像至窗口
	dcMem.DeleteDC();																				//删除绘制的设备
	bmp.DeleteObject();																				//删除绘制的图像
}


void Game::OnBnClickedButtonpause()
{
	//点击“暂停”按钮
	CString text;
	GetDlgItem(IDC_BUTTON_PAUSE)->GetWindowText(text);							//获取按钮控件的文字

	//暂停操作
	if (text == _T("暂停")) {
		KillTimer(FPS_CLOCK);
		KillTimer(UPDATESNAKE_CLOCK);
		GetDlgItem(IDC_BUTTON_PAUSE)->SetWindowText(_T("继续"));
	}
	//继续操作
	if (text == _T("继续")) {
		SetTimer(FPS_CLOCK, 16, NULL);
		SetTimer(UPDATESNAKE_CLOCK, 500 - (mSnake->speed - 1) * 50, NULL);
		GetDlgItem(IDC_BUTTON_PAUSE)->SetWindowText(_T("暂停"));
	}
}


void Game::OnBnClickedButtonBreak()
{
	//点击“退出”按钮
	save();								//保存
	EndDialog(IDPAUSE);					//对话框返回游戏暂停信息
}


BOOL Game::PreTranslateMessage(MSG* pMsg)
{
	//预处理按键信息
	switch (pMsg->wParam)
	{
	case VK_UP:
		if (mSnake->direction != DOWN)
			direction = UP;
		break;
	case VK_DOWN:
		if (mSnake->direction != UP)
			direction = DOWN;
		break;
	case VK_LEFT:
		if (mSnake->direction != RIGHT)
			direction = LEFT;
		break;
	case VK_RIGHT:
		if (mSnake->direction != LEFT)
			direction = RIGHT;
		break;
	case VK_ESCAPE:
		return true;
	case VK_RETURN:
		return true;
	}


	return CDialogEx::PreTranslateMessage(pMsg);
}


void Game::OnTimer(UINT_PTR nIDEvent)
{
	switch (nIDEvent)
	{
	case FPS_CLOCK:
		Invalidate(true);
		break;
	case UPDATESNAKE_CLOCK:
		//添加新食物
		mSnake->addItem();

		
		//移动操作
		if (checkDirection(mSnake->direction, direction)) {				//如果操作方向合法，更新实例的方向
			mSnake->direction = direction;	
		}
		mSnake->move();


		//更新速度
		if (mSnake->speedUp()) {											//检测蛇是否需要提速
			KillTimer(UPDATESNAKE_CLOCK);									//重置时钟
			SetTimer(UPDATESNAKE_CLOCK, 500 - (mSnake->speed - 1) * 50, NULL);
		}


		//更新分数
		CString text;
		text.Format(_T("分数：%d"), mSnake->score);
		GetDlgItem(IDC_STATIC_SCORE)->SetWindowText(text);


		//检测结束
		if (!gameOver && mSnake->gameOver()) {								//游戏结束，关闭窗口
			KillTimer(FPS_CLOCK);
			KillTimer(UPDATESNAKE_CLOCK);
			gameOver = true;												//防止重复弹出窗口
			EndDialog(MessageBox(_T("是否重玩？"), _T("游戏结束！"), MB_OKCANCEL));
		}
		break;
	}


	CDialogEx::OnTimer(nIDEvent);
}


BOOL Game::OnEraseBkgnd(CDC* pDC)
{
	//禁用刷新背景
	return true;
}


void Game::save()
{
	//临时存储难度设置
	int difficuty = GetPrivateProfileInt(_T("SETTING"), _T("DIFFICUTY"), 1, _T("./Setting.ini"));


	//清空文件
	CFile settingFile;
	settingFile.Open(_T("./Setting.ini"), CFile::modeCreate);
	settingFile.Close();

	
	CString temp;

	WritePrivateProfileString(_T("SETTING"), _T("PAUSE"), _T("1"), _T("./Setting.ini"));

	temp.Format(_T("%d"), difficuty);
	WritePrivateProfileString(_T("SETTING"), _T("DIFFICUTY"), temp, _T("./Setting.ini"));

	temp.Format(_T("%d"), mSnake->speed);
	WritePrivateProfileString(_T("SETTING"), _T("SPEED"), temp, _T("./Setting.ini"));

	temp.Format(_T("%d"), mSnake->direction);
	WritePrivateProfileString(_T("SETTING"), _T("DIRECTION"), temp, _T("./Setting.ini"));

	temp.Format(_T("%I64u"), mSnake->body.size());
	WritePrivateProfileString(_T("SNAKE"), _T("BODYAmount"), temp, _T("./Setting.ini"));
	int order = 1;
	for (auto it = mSnake->body.begin(); it != mSnake->body.end(); ++it) {
		CString temp2;
		temp.Format(_T("BODY%dX"), order);
		temp2.Format(_T("%d"), it->first);
		WritePrivateProfileString(_T("SNAKE"), temp, temp2, _T("./Setting.ini"));
		temp.Format(_T("BODY%dY"), order++);
		temp2.Format(_T("%d"), it->second);
		WritePrivateProfileString(_T("SNAKE"), temp, temp2, _T("./Setting.ini"));
	}

	temp.Format(_T("%I64u"), mSnake->item.size());
	WritePrivateProfileString(_T("MAP"), _T("ITEMAmount"), temp, _T("./Setting.ini"));
	order = 1;
	for (auto it = mSnake->item.begin(); it != mSnake->item.end(); ++it) {
		CString temp2;
		temp.Format(_T("ITEM%dX"), order);
		temp2.Format(_T("%d"), it->first);
		WritePrivateProfileString(_T("MAP"), temp, temp2, _T("./Setting.ini"));
		temp.Format(_T("ITEM%dY"), order++);
		temp2.Format(_T("%d"), it->second);
		WritePrivateProfileString(_T("MAP"), temp, temp2, _T("./Setting.ini"));
	}
}


void Game::OnClose()
{
	save();
	EndDialog(IDCLOSE);
}


bool Game::checkDirection(int snakeDirection, int inputDirection)
{
	switch (inputDirection)
	{
	case UP:
		if (snakeDirection != DOWN) return true;
		break;
	case DOWN:
		if (snakeDirection != UP) return true;
		break;
	case LEFT:
		if (snakeDirection != RIGHT) return true;
		break;
	case RIGHT:
		if (snakeDirection != LEFT) return true;
		break;
	}
	return false;
}
